#include "Terrain.h"


Terrain::Terrain(void)
{
	mySeed();
	
	for (int i = 0; i < WIDTH/STEP; i++)
	{
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			map[i][j] = 0;
		}
	}

	cellCountX = WIDTH/STEP; cellCountY = HEIGHT/STEP;

	GeneratePerlinNoise();

	for (int i = 0; i < WIDTH/STEP; i++)
	{
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			if (0 == map[i][j])
			{
				map[i][j] = 0.1;
			}
		}
	}
}


Terrain::~Terrain(void)
{
}


float Terrain::GetNoise(int i, int j)
{
	return (SimplexNoise1234::noise(i, j) + 1) / 2;
}

bool Terrain::validCell(int inX, int inY) {
	if (inX < 0 || inX >= cellCountX ||
		inY < 0 || inY >= cellCountY)
		return false;
	if (GetTerrainType(inY, inX) == OCCUPIED)
		return false;
	return true;
}

vertex Terrain::GetWorldPoint(int i, int j)
{
	// Returns the center of the cell in world coordinates
	float x = (i * STEP) - (WIDTH/2.0) + (STEP/2.0);
	float y = (j * STEP) - (WIDTH/2.0) + (STEP/2.0);

	return vertex(x, y);
}

vertex Terrain::GetTerrainPoint(vertex position)
{
	int i = floor((position.x+(WIDTH/2)) / STEP);
	int j = floor((position.y+(HEIGHT/2)) / STEP);

	i = max(0, min(WIDTH/STEP, i));
	j = max(0, min(HEIGHT/STEP, j));

	return vertex(i,j);
}

Terrain::TerrainType Terrain::GetTerrainType(vertex position)
{
	int i = floor((position.x+(WIDTH/2)) / STEP);
	int j = floor((position.y+(HEIGHT/2)) / STEP);

	i = max(0, min(WIDTH/STEP, i));
	j = max(0, min(HEIGHT/STEP, j));

	return GetTerrainType(j,i);
}


Terrain::TerrainType Terrain::GetTerrainType(int i, int j)
{
	float x = map[i][j];
	
	if (x <= 0)
	{
		return TerrainType::OCCUPIED;
	}
	else if (x < 0.40)
	{
		return TerrainType::ROAD;
	}
	else if (x >= 0.60)
	{
		return TerrainType::GRAVEL;
	}
	else 
	{
		return TerrainType::DIRT;
	}

}

void Terrain::Occupy(vertex position)
{
	int i = floor((position.x+(WIDTH/2)) / STEP);
	int j = floor((position.y+(HEIGHT/2)) / STEP);

	i = max(0, min(WIDTH/STEP, i));
	j = max(0, min(HEIGHT/STEP, j));

	Occupy(j,i);
}

void Terrain::Evict(vertex position)
{
	int i = floor((position.x+(WIDTH/2)) / STEP);
	int j = floor((position.y+(HEIGHT/2)) / STEP);

	i = max(0, min(WIDTH/STEP, i));
	j = max(0, min(HEIGHT/STEP, j));

	Evict(j,i);
}

void Terrain::Occupy(int i, int j)
{
	if (0 < map[i][j])
	{
		map[i][j] = -1*map[i][j];
	}
}

void Terrain::Evict(int i, int j)
{
	if (0 > map[i][j])
	{
		map[i][j] = -1*map[i][j];
	}
}

void Terrain::Draw()
{
	glBegin(GL_QUADS);
	
	for (int i = 0; i < WIDTH/STEP; i++)
	{
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			DrawCell(i,j);
		}
	}

	glEnd();
}

void Terrain::DrawCell(int i, int j)
{
	// Colors
	float x = map[i][j];
	switch (GetTerrainType(i, j))
	{
		case DIRT:
			//glColor3f(0, x, 0);
			glColor3f(0, x, 0);
			break;
		case ROAD:
			//glColor3f(0, 0, x);
			glColor3f(0.5+x,0.4+x, 0);
			break;
		case GRAVEL:
			//glColor3f(x, 0, 0);
			glColor3f( x-0.4, x-0.3, x-0.3);
			break;
		case OCCUPIED:
			glColor3f(0, 0, 0);
			break;
		default:
			//glColor3f(x, x, x);
			glColor3f(0.2 + x, 0.2 + x, 0.2 + x);
			break;
	}

	//glColor3f(x, x, x);

	//glColor3f(i/20.0, 0, j/20.0);


	float left = (j*STEP) - (WIDTH/2);
	float right = left + STEP;
	float top = (i*STEP) - (HEIGHT/2);
	float bottom = top + STEP;

	glVertex2f(left, top); 
	glVertex2f(left, bottom);
	glVertex2f(right, bottom);
	glVertex2f(right, top);
}

void Terrain::NextPermutation()
{
	bool test = false;
	test = SimplexNoise1234::NextPermutation();
}

float Terrain::interpolate(float x, float y, float alpha)
{
	return ((1 - alpha)*x) + (alpha *y);
}

void Terrain::GenerateSmoothNoise(int octave)
{
	int period = 1 << octave; // 2 ^ octave
   float frequency = 1.0 / period;
   
   for (int i = 0; i < WIDTH/STEP; i++)
   {
      //calculate the horizontal sampling indices
      int sample_i0 = (i / period) * period;
      int sample_i1 = (sample_i0 + period) % (WIDTH/STEP); //wrap around
      float horizontal_blend = (i - sample_i0) * frequency;
 
      for (int j = 0; j < HEIGHT/STEP; j++)
      {
         //calculate the vertical sampling indices
         int sample_j0 = (j / period) * period;
         int sample_j1 = (sample_j0 + period) % HEIGHT/STEP; //wrap around
         float vertical_blend = (j - sample_j0) * frequency;
 
         //blend the top two corners
         float top = interpolate(GetNoise(sample_i0,sample_j0),
					             GetNoise(sample_i1,sample_j0), 
								 horizontal_blend);
 
         //blend the bottom two corners
         float bottom = interpolate(GetNoise(sample_i0,sample_j1),
						            GetNoise(sample_i1,sample_j1), 
									horizontal_blend);
 
         //final blend
         smoothNoise[octave][i][j] = interpolate(top, bottom, vertical_blend);
      }
   }
}

void Terrain::GeneratePerlinNoise()
{
	float persistance = 0.7;

	// Generate smooth noise
	for (int i = 0; i < OCTAVES; i++)
	{
		NextPermutation();
		GenerateSmoothNoise(i);
	}

	float amplitude = 1.0;
	float totalAmplitude = 0.0;
 
	//blend noise together
	for (int octave = OCTAVES - 1; octave >= 0; octave--)
	{
		amplitude *= persistance;
		totalAmplitude += amplitude;
 
		for (int i = 0; i < WIDTH/STEP; i++)
		{
			for (int j = 0; j < HEIGHT/STEP; j++)
			{
				map[i][j] += smoothNoise[octave][i][j] * amplitude;
			}
		}
	}
 
	//normalisation
	for (int i = 0; i < WIDTH/STEP; i++)
	{
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			map[i][j] /= totalAmplitude;
		}
	}
}


void Terrain::Load_Test_Map_1()
{
	float stripe[] = {0.0, 0.0, 0.0, 0.0,
					  0.5, 0.5, 0.5, 0.5,
					  0.5, 1.0, 1.0, 1.0,
					  1.0, 1.0, 0.5, 0.5,
					  0.5, 0.5, 0.5, 0.5};
	int s;

	for (int i = 0; i < WIDTH/STEP; i++)
	{
		s = i;
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			map[i][j] = stripe[(s+j)%20];
		}
	}
}

void Terrain::Load_Test_Map_2()
{
	for (int i = 0; i < WIDTH/STEP; i++)
	{
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			if ( i == 10) {
				if (j >5 && j < 15) map[i][j] = 1;
				else map[i][j] = 0.001;
			}
			else if (i <= 18) {
				if (j == 5) map[i][j] = -1;
				else map[i][j] = 0.5;
			}
			else map[i][j] = 0.5;
		}
	}
	map[10][5] = -1;
}

void Terrain::Load_Test_Map_3()
{
	for (int i = 0; i < WIDTH/STEP; i++)
	{
		for (int j = 0; j < HEIGHT/STEP; j++)
		{
			map[i][j] = 0.5;
		}
	}
}